跳到主要内容

Java Servlet API 学习~

Request 的功能

获取请求行数据

GET /data/info.html HTTP/1.1

常用方法

// 获取请求方式
System.out.println(req.getMethod());

// 获取虚拟目录(就是这个项目名,因为一个容器里可以部署多个项目,如果是根目录就返回空)
System.out.println(req.getContextPath());

// 获取 Servlet 路径
System.out.println(req.getServletPath());

// 直接拿到全部参数字符串(例如 name=alsritter&id=100&gender=male 这样的形式)
System.out.println(req.getQueryString());

// 返回项目名 + Servlet 路径 (注意这个是 URI 下面那个是 URL 两个是有区别的)
System.out.println(req.getRequestURI());

// 返回完整的路径(http://localhost/项目名/Servlet路径)
System.out.println(req.getRequestURL());

// 获取 HTTP 的协议版本
System.out.println(req.getProtocol());

// 获取客户端的地址
System.out.println(req.getRemoteAddr());

获取请求头数据

Request Headers 实例

Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: alsritter.icu
Pragma: no-cache
Referer: http://alsritter.icu/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36

常用方法:

// 获取一个 Header 属性,参数是属性名(参考上面的 Request Headers 实例)
System.out.println(req.getHeader("User-Agent"));
/*
* 注意 Header 的 Referer 属性,这个属性是告诉服务器当前请求从哪里来,
* 当直接通过浏览器访问这个链接时(非跳转)这个值为 null
*/



// 获取所有的请求头名称 getHeaderNames()

// 这个 getHeaderNames() 返回的对象 Enumeration<String> 类似一个迭代器
Enumeration<String> headerNames = req.getHeaderNames();
// 这个 hasMoreElements 就像迭代器的 hasNext() 一样判断集合中是否有元素,如果有元素可以迭代,就返回true
while(headerNames.hasMoreElements()){
// headerNames 的值就是一些属性名称
String name = headerNames.nextElement();
// 根据属性名取得对应的属性值
String value = req.getHeader(name);

System.out.println(name + "---" + value);
}

获取请求体数据

只要 POST 请求方式才有请求体 Tomcat 会把请求体封装成流对象,所以需要先获取流再从流中拿数据

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
BufferedReader reader = req.getReader();
char[] buffer = new char[1024];
int len;
StringBuilder str = new StringBuilder();
while ((len = reader.read(buffer)) != -1) {
str.append(new String(buffer,0,len));
}

System.out.println(str);
}

其他通用方法

获取请求参数

上面讲的两个取得参数(从 Body 或者 GET 请求的参数)都比较麻烦,一般获取参数使用的是下面这个 getParameter 方法

// 还有几个获取参数封装成 Map 或者数组的方法,但是都是大同小异
System.out.println(req.getParameter("name"));

处理中文乱码

Tomcat 8 会自动处理 GET 方式传的参数乱码问题,但是 POST 的方式还有乱码

因为 GET 请求是通过 URL 一起传进来的,而 POST 则是通过流中取得的数据(具体看上面),所以需要设置流的字符集

req.setCharacterEncoding("utf-8");
System.out.println(req.getParameter("name"));

请求转发

请求转发就是一种在服务器内部(不能访问外部网站)资源跳转的方式,就是 AServlet 跳转到 BServlet 的方式

步骤: 1、 通过 Request 对象获取请求转发器对象:

// 方法的返回类型 RequestDispatcher
RequestDispatcher getRequestDispatcher(String path)

2、然后再通过这个请求转发器对象来进行转发

req.getRequestDispatcher("/temp2").forward(req,resp);

共享数据

前面讲了请求转发就是 AServlet 跳转到 BServlet 先在 AServlet 里面处理一些问题,然后再跳转到 BServlet 接着处理,那就需要告诉 BServlet 一些在 AServlet 已经处理的数据,这就涉及到资源共享的问题了

这里就涉及到一个作用域的问题(毕竟有多个请求,要是全局的,那 Servlet 就无法分别哪个是哪个的数据了)

1、域对象:一个有作用范围的对象,可以在范围内共享数据

2、Request 域:代表一次请求的范围,就是转发过去的 Request 对象里面包含了这个数据

3、Session 域:同上,就是一个会话的作用域,至于怎么维持这个会话的参考 HTTP 协议学习那篇笔记

传递数据的方法

// 存储数据
req.setAttribute("key","这是传过去的数据");

// 通过键获取值
req.getAttribute("key");

// 通过键移除值
req.removeAttribute("key");

Response 的功能

功能:设置响应消息

设置响应行

格式:HTTP/1.1 200 ok 设置状态码:setStatus(int sc)

设置响应头

setHeader(String name, String value)

设置响应体

使用步骤: 1、获取输出流 字符输出流:printWriter getWriter()

字节输出流:ServletOutputStream getOutputStream()

2、使用输出流,将数据输出到客户端浏览器

重定向操作

结合上面的转发资源,这里对重定向和转发进行区分

转发的的特点

1、转发的地址栏路径不变(因为这是服务器内部传递资源给其他的 Servlet 的机制) 2、转发只能在当前服务器进行 3、转发从头到尾都只是一次请求

重定向的特点

1、地址栏发送变化 2、重定向可以访问其它站点(服务器)的资源 3、重定向是两次请求

通用的写法

在 HTTP 那篇文章说过重定向操作就是返回一个 300~399 这个范围的状态码,客户端收到就会自动向这个响应头中的 location 携带的路径发起请求,所以如下操作

@WebServlet("/temp")
public class TempServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//1. 设置状态码 302 告诉浏览器需要重定向
resp.setStatus(302);
//2. 设置响应头为 location,并说明重定向地址
resp.setHeader("location","/temp2");
}
}

@WebServlet("/temp2")
public class Temp2Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("重定向成功");
}
}

可以 F12 看一下第一个 temp 请求的响应码是 302 同时页面地址也成了

http://localhost:8080/temp2

aZoKjP.png

封装的方法

同上面的获取参数一样,JavaServlet 封装了这个重定向的操作

resp.sendRedirect("/temp2");

输出数据案例

字符数据

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1. 获取字符输出流(注意是 response 获取的)
PrintWriter writer = resp.getWriter();
// 2. 输出数据,不需要刷新和关闭,因为 Response 完成一次响应后会自动销毁,再此之前会自动把流关闭掉
writer.write("<h1>hello response !</h1>");
}

中文乱码的问题

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 获取流对象之前,设置流的默认编码(注意,这是告诉流对象的,不是响应头的)
resp.setCharacterEncoding("utf-8"); // 设置流的编码其实可以省略,因为默认就是 utf-8

// 设置响应头的编码,告诉客户端应该用什么编码来解析
resp.setHeader("Content-Type","text/html;charset=utf-8");

// 同理,这种这么常用的响应头属性都有一个方法用来调用
resp.setContentType("text/html;charset=utf-8");

// 1. 获取字符输出流(注意是 response 获取的)
PrintWriter writer = resp.getWriter();

// 2. 输出数据,不需要刷新和关闭,因为 Response 完成一次响应后会自动销毁,再此之前会自动把流关闭掉
writer.write("你好,这是响应消息");
}

字节数据

与上面的字符数据原理是一样的,只不过获取的是字节流对象

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=utf-8");

// 1. 获取字节输出流
ServletOutputStream outputStream = resp.getOutputStream();

// 2. 输出数据
outputStream.write("这是字节流输出的数据".getBytes());
// 还可以直接在输出的文本设置编码
outputStream.write("这是字节流输出的数据".getBytes("utf-8"));
}

生成验证码

作用:防止恶意注册(例如开一个死循环一直注册)

@WebServlet("/temp")
public class TempServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
int width = 100;
int height = 50;

// 设置响应类型
resp.setContentType("image/jpg");

//1. 创建对象,在内存中生成图片(验证码图片对象)
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);

//2. 填充信息到图片
//2.1 填充背景颜色(默认是纯黑色)
Graphics g = image.getGraphics();
// 设置画笔的颜色
g.setColor(Color.pink);
// 填充的位置(x,y),然后是宽高
g.fillRect(0, 0, width, height);

//2.2 绘制一个边框
g.setColor(Color.blue);
// 因为边框默认有 1 像素的宽度,所以需要减 1
g.drawRect(0, 0, width - 1, height - 1);

//2.3 写字符串(可以写一个随机位置)
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuffer strCode = new StringBuffer(); // 用来存储这个验证码

// 生成随机的字符
Random ran = new Random();
for (int i = 0; i < 4; i++) {
int index = ran.nextInt(str.length());
// 获取字符
char ch = str.charAt(index);
strCode.append(ch);

// 随机位置
int tempWidth = ran.nextInt(width / 4);
int tempHeight = ran.nextInt(height);
// 因为 横着有顺序,所以随机的位置最多 width / 4
g.drawString(ch + "",
((tempWidth > 20 || tempWidth < 5) ? 10 : tempWidth) + (width / 4) * i,
(tempHeight > (height/5)*4 || tempHeight < (height/5)) ? 10 : tempHeight );
}

//2.4 画干扰线(两点确定一条线)
g.setColor(Color.red);
for (int i = 0; i < 5; i++) {
g.drawLine(ran.nextInt(width),ran.nextInt(height),ran.nextInt(width),ran.nextInt(height));
}

//3. 输出图片到响应体里
ImageIO.write(image, "jpg", resp.getOutputStream());
// 可以直接取到这个验证码
System.out.println(strCode);

}
}

ServletContext

ServletContext 对象代表整个 web 应用 功能: 1、获取 MIME 类型 2、域对象:共享数据 3、获取文件的真实(服务器)路径

ServletContext 可以通过 Request 获取到

ServletContext servletContext = req.getServletContext();

也可以通过 HttpServlet 获取到(继承了这个对象)

this.getServletContext()

获取 MIME 类型

MIME类型:在互联网通信过程中定义的一种文件数据类型 格式:大类型/小类型(text/html)

而这个 Mime 类型也可以通过 ServletContext 来动态获取

其会通过文件后缀自动判断这个文件的类型,并返回对应的 Mime 在 Tomcat 的配置文件夹下(conf)的 web.xml 里可以设置这个对应关系

req.getServletContext().getMimeType("fileName")

获取项目路径

补充个路径相关的问题:

获取当前项目名路径

因为在同一个 Application Server 里面可以部署多个项目,所以一般写路径还需要把项目名加上 但是如果直接写死,后期项目名要更改就需要手动改,因此一般使用 req.getContextPath() 获取项目名来动态拼接路径

req.getContextPath()